"""
用户
"""

from typing import Optional

import utils.enum_utils as enum
from components.exceptions import BusinessException
from db import user_db
from schema.db_schema import UserDBModel
from schema.request_schema import UserRegisterRequest
from schema.response_schema import AuthenticationData
from schema.view_schema import JWTPayloadViewModel, UserViewModel
from utils.encrypt_utils import hash_with_sha256
from utils.generator_utils import StringGenerator
from utils.mock_utils import DATETIME


class UserAuthenticator:
    """ 用户 认证器 """

    @staticmethod
    def get_token_by_user_code(
        user_code: str,
        token_max_age: Optional[int] = None,
    ) -> str:
        """
        根据用户编号获取 Token

        Args:
            user_code (str): 用户编号
            token_max_age (Optional[int]): Token 过期天数

        Returns:
            str: Token
        """

        from biz.token_biz import TokenHandler

        token: str = TokenHandler.get_jwt(
            user_code=user_code,
            token_max_age=token_max_age,
        )

        return token

    @staticmethod
    def get_user_by_user_code(user_code: str) -> UserViewModel:
        """
        根据用户编号获取用户信息

        Args:
            user_code (str): 用户编号

        Returns:
            UserViewModel: 用户模型（视图模型）
        """

        user_info: dict = user_db.user.find_one({"code": user_code})

        if not user_info:
            raise BusinessException(f"USER_CODE <{user_code}> 未查询到用户信息")

        user_view_model: UserViewModel = UserViewModel(**user_info)

        return user_view_model

    @classmethod
    def get_user_by_token(
        cls,
        token: str,
    ) -> UserViewModel:
        """
        根据 Token 获取用户信息

        Args:
            token (str): Token

        Returns:
            UserViewModel: 用户模型（视图模型）
        """

        from biz.token_biz import TokenHandler

        payload: JWTPayloadViewModel = TokenHandler.get_payload_from_jwt(token)
        user_view_model: UserViewModel = cls.get_user_by_user_code(payload.user_code)

        return user_view_model

    @classmethod
    def get_authenticated_data(
        cls,
        user_view_model: UserViewModel,
        token_max_age: Optional[int] = None,
    ) -> AuthenticationData:
        """ 获取用户认证数据 """

        token: str = cls.get_token_by_user_code(user_view_model.code, token_max_age)
        authentication_response: AuthenticationData = AuthenticationData(
            token=token,
            user_code=user_view_model.code,
            nickname=user_view_model.nickname,
        )

        return authentication_response


class UserRegister:
    """ 用户 注册器 """

    class Validator:
        """ 校验器 """

        @staticmethod
        def validate_username_password(
            username: str,
            password: str,
            **kwargs,
        ) -> None:
            """
            校验用户名和密码是否相同

            Args:
                username (str): 用户名
                password (str): 密码

            Raises:
                BusinessException: 用户名和密码不能相同
            """

            if username == password:
                raise BusinessException("用户名和密码不能相同")

        @staticmethod
        def validate_same_password(
            password: str,
            re_password: str,
            **kwargs,
        ) -> None:
            """
            校验密码是否一致

            Args:
                password (str): 密码
                re_password (str): 确认密码

            Raises:
                BusinessException: 密码不一致
            """

            if password != re_password:
                raise BusinessException("密码不一致")

        @staticmethod
        def validate_duplicate_username(
            username: str,
            **kwargs,
        ) -> None:
            """
            校验用户名是否已存在

            Args:
                username (str): 用户名

            Raises:
                BusinessException: 用户名已存在
            """

            if user_db.user.count_documents({"username": username}):
                raise BusinessException("用户名已存在")

        @classmethod
        def validate(cls, request: UserRegisterRequest) -> None:
            """
            校验用户注册信息

            Args:
                request (UserRegisterRequest): 用户注册请求体

            Raises:
                BusinessException 1: 用户名和密码不能相同
                BusinessException 2: 密码不一致
                BusinessException 3: 用户名已存在
            """

            for _validator in (
                cls.validate_username_password,  # ->   1. 校验用户名和密码是否相同
                cls.validate_same_password,  # ->       2. 校验密码是否一致
                cls.validate_duplicate_username,  # ->  3. 校验用户名是否已存在
            ):
                _validator(**request.model_dump())

    class Creator:
        """ 创建器 """

        @staticmethod
        def get_encrypted_password(password: str) -> str:
            """
            获取加密后的密码

            Args:
                password (str): 密码

            Returns:
                str: 加密后的密码
            """

            return hash_with_sha256(password)

        @staticmethod
        def get_default_nickname(username: str) -> str:
            """
            获取默认昵称

            - 默认昵称 = "用户：" + 用户名后 3 位

            Args:
                username (str): 用户名

            Returns:
                str: 默认昵称

            >>> UserRegister.Creator.get_default_nickname("1234567")
            "用户：567"
            """

            return f"用户：{username[-3:]}"

        @staticmethod
        def get_default_gender() -> int:
            """
            获取默认性别

            - 默认性别 = "未知"

            Returns:
                str: 默认性别

            >>> UserRegister.Creator.get_default_gender()
            "USER_240204031806448"
            """

            return enum.Gender.UNKNOWN.value

        @staticmethod
        def get_generated_code() -> str:
            """
            获取生成的用户编号

            SwaggerTag.name + StringGenerator.generate_unique_code 生成的数据

            Returns:
                str: 用户编号

            >>> UserRegister.Creator.get_generated_code()
            "USER_240204031806448"
            """

            code: str = StringGenerator.generate_unique_code(prefix=enum.SwaggerTag.USER.name)

            return code

        @staticmethod
        def get_now_time() -> str:
            """
            获取当前时间

            Returns:
                str: 当前时间

            >>> UserRegister.Creator.get_now_time()
            "2024-02-02 17:27:53"
            """

            return DATETIME.now_str()

        @classmethod
        def get_union_user_model(
            cls,
            request: UserRegisterRequest,
        ) -> UserDBModel:
            """
            获取完成组装的用户模型（数据库模型）

            Args:
                request (UserRegisterRequest): 用户注册请求体

            Returns:
                UserDBModel: 用户模型（数据库模型）
            """

            user_db_model: UserDBModel = UserDBModel(
                username=request.username,
                password=cls.get_encrypted_password(request.password),
                nickname=cls.get_default_nickname(request.username),
                code=cls.get_generated_code(),
                gender=cls.get_default_gender(),
                last_active=cls.get_now_time(),
            )

            return user_db_model

        @staticmethod
        def create_user(user_db_model: UserDBModel) -> None:
            """
            创建用户

            Args:
                user_db_model (UserDBModel): 用户模型（数据库模型）
            """

            user_db.user.insert_one(user_db_model.model_dump())

        @classmethod
        def create(cls, request: UserRegisterRequest) -> UserViewModel:
            """
            创建用户信息

            Args:
                request (UserRegisterRequest): 用户注册请求体

            Returns:
                UserDBModel: 用户模型（数据库模型）
            """

            # 1. 获取完成组装的用户模型（数据库模型）
            user_db_model: UserDBModel = cls.get_union_user_model(request)

            # 2. 创建用户
            cls.create_user(user_db_model)

            # 3. 将数据库模型转换成视图模型
            user_view_model: UserViewModel = UserViewModel(**user_db_model.model_dump())

            return user_view_model

    @classmethod
    def register(cls, request: UserRegisterRequest) -> AuthenticationData:
        """
        注册用户

        Args:
            request (UserRegisterRequest): 用户注册请求体

        Returns:
            dict: 注册结果
        """

        # 1. 参数校验
        cls.Validator.validate(request)

        # 2. 创建用户
        user_view_model: UserViewModel = cls.Creator.create(request)

        # 3. 生成 Token
        authentication_data: AuthenticationData = UserAuthenticator.get_authenticated_data(user_view_model)

        return authentication_data
